using System;
using System.Collections.Generic;
using FluorineFx;
using FluorineFx.AMF3;

namespace SharedCloud
{
    public class RevisionTracker
    {
        private struct ObjPointer
        {
            public int cid, lid;
            public ObjPointer(int cid, int lid)
            {
                this.cid = cid;
                this.lid = lid;
            }
        }

        private ASObjectCache cache = new ASObjectCache();
        private List<ObjPointer> revisions = new List<ObjPointer>();

        public RevisionTracker()
        {
        }
        public RevisionTracker(List<ASObject> initialHeaders) : this()
        {
            foreach (ASObject header in initialHeaders)
                Commit(header);
        }

        public ASObject Update(int lastRevision)
        {
            ASObject update = new ASObject("SharedCloud::Update");
            update["revision"] = revisions.Count - 1;

            //find all the changes that have been made since the last update,
            //  but only include each object once.
            List<ObjPointer> changes = new List<ObjPointer>();
            for (int i = lastRevision + 1; i < revisions.Count; i++)
                if (!changes.Contains(revisions[i]))
                    changes.Add(revisions[i]);
            
            //retrieve the changed objects from the cache
            ArrayCollection objs = new ArrayCollection();
            foreach (ObjPointer change in changes)
                objs.Add(cache[change.cid, change.lid]);
            update["headers"] = objs;

            return update;
        }

        public int Commit(ASObject header)
        {
            ASObject vo = header["vo"] as ASObject;
            int cid = Convert.ToInt32(vo["clientID"]);
            int lid = Convert.ToInt32(vo["localID"]);

            ASObject cachedHeader = cache[cid, lid];
            CopyASObject(header, cachedHeader);

            ObjPointer pntr = new ObjPointer(cid, lid);
            revisions.Add(pntr);
            return revisions.Count - 1;
        }

        protected void CopyASObject(ASObject source, ASObject dest)
        {
            dest.TypeName = source.TypeName;
            foreach (object key in source.Keys)
            {
                if (source[key] is ASObject)
                {
                    if(dest[key] == null || !(dest[key] is ASObject))
                        dest[key] = new ASObject();
                    CopyASObject((ASObject)source[key], (ASObject)dest[key]);
                }
                else if (source[key] is ArrayCollection)
                {
                    if (dest[key] == null || !(dest[key] is ArrayCollection))
                        dest[key] = new ArrayCollection();
                    CopyArrayCollection((ArrayCollection)source[key], (ArrayCollection)dest[key]);
                }
                else
                {
                    dest[key] = source[key];
                }
            }
        }
        protected void CopyArrayCollection(ArrayCollection source, ArrayCollection dest)
        {
            dest.Clear();
            foreach (object item in source)
            {
                if (item is ASObject)
                {
                    ASObject newItem = new ASObject();
                    CopyASObject((ASObject)item, newItem);
                    dest.Add(newItem);
                }
                else if (item is ArrayCollection)
                {
                    ArrayCollection newColl = new ArrayCollection();
                    CopyArrayCollection((ArrayCollection)item, newColl);
                    dest.Add(newColl);
                }
                else
                {
                    dest.Add(item);
                }
            }
        }
    }
}
